<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>JSDoc: Source: GnssLib.js</title>

    <script src="scripts/prettify/prettify.js"> </script>
    <script src="scripts/prettify/lang-css.js"> </script>
    <!--[if lt IE 9]>
      <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
    <![endif]-->
    <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
    <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
</head>

<body>

<div id="main">

    <h1 class="page-title">Source: GnssLib.js</h1>

    



    
    <section>
        <article>
            <pre class="prettyprint source linenums"><code>
const GPST0 = new Date(Date.UTC(1980, 1, 6)).getTime()
const BDT0 = new Date(Date.UTC(2006, 1, 1)).getTime()
const UTC0 = new Date(Date.UTC(1970, 1, 1)).getTime()
const GST0 = new Date(Date.UTC(1999, 8, 22)).getTime()

const WEEK_SEC = 7 * 24 * 60 * 60
const WEEK_MSEC = WEEK_SEC * 1000
const TZ_OFFSET = new Date().getTimezoneOffset() * 60 * 1000

const PI = 3.1415926535897932           /* pi */
const D2R = (PI / 180.0)          /* deg to rad */
const R2D = (180.0 / PI)          /* rad to deg */
const RE_WGS84 = 6378137.0              /* earth semimajor axis (WGS84) (m) */
const FE_WGS84 = (1.0 / 298.257223563)  /* earth flattening (WGS84) */


const GPS = 'G'
const BDS = 'C'
const GLO = 'R'
const GAL = 'E'
const SBAS = 'S'
const QZSS = 'J'
const IRNSS = 'I'

/**
 * Glonass系统各个卫星的载频索引/频率编号
 * @see http://www.csno-tarc.cn/glonass/constellation
 * @private
 */
const GLONASS_FREQ = [
    1, -4, 5, 6, 1,
    -4, 5, 6, -2, -7,
    0, -1, -2, -7, 0,
    -1, 4, -3, 3, 2,
    4, -3, 3, 2]

const GPST_LEAPS = [ /* leap seconds (y,m,d,h,m,s,utc-gpst) */
    [2017, 1, 1, -18],
    [2015, 7, 1, -17],
    [2012, 7, 1, -16],
    [2009, 1, 1, -15],
    [2006, 1, 1, -14],
    [1999, 1, 1, -13],
    [1997, 7, 1, -12],
    [1996, 1, 1, -11],
    [1994, 7, 1, -10],
    [1993, 7, 1, -9],
    [1992, 7, 1, -8],
    [1991, 1, 1, -7],
    [1990, 1, 1, -6],
    [1988, 1, 1, -5],
    [1985, 7, 1, -4],
    [1983, 7, 1, -3],
    [1982, 7, 1, -2],
    [1981, 7, 1, -1],
]

/**
 * 返回GPST周和周内秒距离UTC起始时（1970-1-1 0:0:0）的微秒数
 * @param {*} week GPS周
 * @param {*} sow GPS周内秒
 * @returns 距离UTC起始时（1970-1-1 0:0:0）的微秒数。关于非法参数，总是返回GPST的起始时间（1980-1-6 0:0:0）的微秒数
 * @static
 */
Date.GPST = function (week, sow) {
    let w = parseInt(week) || 0
    let s = parseFloat(sow) || 0

    if (w &lt; 0 || s > WEEK_SEC || typeof week !== 'number' || typeof sow !== 'number' ||
        w > Number.MAX_SAFE_INTEGER || String(week).indexOf('.') != -1) {
        w = 0
        s = 0
    }

    let secondsBD = w * WEEK_MSEC + s * 1000;
    let total = GPST0 + secondsBD;
    return gpst2utc(new Date(total - UTC0).toUTC()) - TZ_OFFSET
}

/**
 * 返回BD周和周内秒距离UTC起始时（1970-1-1 0:0:0）的微秒数
 * @param {*} week BD周
 * @param {*} sow BD周内秒
 * @returns 距离UTC起始时（1970-1-1 0:0:0）的微秒数。关于非法参数，总是返回BDT的起始时间（2006-1-1 0:0:0）的微秒数
 */
Date.BDT = function (week, sow) {
    let w = parseInt(week) || 0
    let s = parseFloat(sow) || 0

    if (w &lt; 0 || s > WEEK_SEC || typeof week !== 'number' || typeof sow !== 'number' ||
        w > Number.MAX_SAFE_INTEGER || String(week).indexOf('.') != -1) {
        w = 0
        s = 0
    }

    let secondsBD = w * WEEK_MSEC + s * 1000;
    let total = BDT0 + secondsBD;

    if (w != 0 &amp;&amp; s != 0) {
        let gpst = bdt2gpst(new Date(total - UTC0).toUTC())
        return gpst2utc(new Date(gpst)) - TZ_OFFSET
    } else {
        return total - UTC0
    }
}

/**
 * 返回Galileo周和周内秒距离UTC起始时（1970-1-1 0:0:0）的微秒数
 * @param {*} week BD周
 * @param {*} sow BD周内秒
 * @returns 距离UTC起始时（1970-1-1 0:0:0）的微秒数。关于非法参数，总是返回GST的起始时间（1999-8-22 0:0:0）的微秒数
 */
Date.GST = function (week, sow) {
    let w = parseInt(week) || 0
    let s = parseFloat(sow) || 0

    if (w &lt; 0 || s > WEEK_SEC || typeof week !== 'number' || typeof sow !== 'number' ||
        w > Number.MAX_SAFE_INTEGER || String(week).indexOf('.') != -1) {
        w = 0
        s = 0
    }

    let secondsBD = w * WEEK_MSEC + s * 1000;
    let total = GST0 + secondsBD;
    let gst = new Date(total - UTC0).toUTC();
    return gst2utc(gst) - TZ_OFFSET
}

/**
 * 将当前时间转换成Galileo系统时间
 * @param {*} t 当前对象的时间类型
 * @returns 返回Galileo时间系统对象
 */
Date.prototype.toGST = function () {
    return new Date(utc2gst(this.toUTC()))
}

/**
 * 将当前时间转换成GPS系统时间
 * @param {*} t 当前对象的时间类型
 * @returns 返回GPS时间系统对象
 */
Date.prototype.toGPST = function () {
    return new Date(utc2gpst(this.toUTC()))
}

/**
 * 将当前时间转换成BD系统时间
 * @param {*} t 当前对象的时间类型
 * @returns 返回北斗时间系统对象
 */
Date.prototype.toBDT = function () {
    return new Date(utc2bdt(this.toUTC()))
}

/**
 * 将当前时间转换成UTC时间
 * @param {*} t 当前对象的时间类型
 * @returns 返回UTC时间系统对象
 */
Date.prototype.toUTC = function () {
    return new Date(this.getTime() + TZ_OFFSET)
}

/**
 * 将当前时间转换成UTC(SU)时间
 * @param {*} t 当前对象的时间类型
 * @returns 返回UTC(SU)时间系统对象
 */
Date.prototype.toUTCSU = function () {
    return new Date(this.toUTC().getTime() - 3 * 3600 * 1000)
}

/**
 * 获取当前时间的年积日
 * @returns 具体的年积日
 */
Date.prototype.getDayOfYear = function () {
    let y = this.getUTCFullYear()
    let base = new Date(Date.UTC(y, 0, 1))

    let diff = (this.getTime() - base.getTime())
    let day = parseInt(diff / 1000 / 86400) + 1
    return day
}

/**
 * 获取当前时间的天内秒
 * @returns 具体的天内秒
 */
Date.prototype.getSecondsOfDay = function () {
    let base = new Date(Date.UTC(this.getUTCFullYear(), this.getUTCMonth(), this.getUTCDate()))
    let diff = (this.getTime() - base.getTime()) / 1000
    return (diff % 86400)
}

/**
 * 根据JS时间戳获取GPS的跳秒数
 * @param {*} utc UTC时区的JS时间戳
 * @returns GPS跳秒
 */
function getGPSLeapSecond(utc) {
    let u = parseInt(utc);
    let leapseconds = 0
    if (u &lt;= 0 || typeof utc !== 'number') {
        return leapseconds;
    }

    for (let i = 0; i &lt; GPST_LEAPS.length; i++) {
        let leap = GPST_LEAPS[i]
        let t = new Date(Date.UTC(leap[0], leap[1], leap[2])).getTime()
        if (u - t > 0) {
            leapseconds = Math.abs(leap[3])
            break;
        }
    }

    return leapseconds
}

/**
 * 根据Glonass 卫星号返回该卫星号对应的载频编号（K）/频率索引(-7~6)
 * @param {*} sateliteNumber Glonass卫星号（1~24）
 * @returns 对应的载频编号/频率索引，错误数据返回undefined
 * @see https://www.cnblogs.com/caimagic/p/11754470.html
 */
function getGlonassFrequency(sateliteNumber) {
    let sn = parseInt(sateliteNumber)
    if (!sn || sn &lt; 0 || sn > 24) {
        return undefined
    }
    return GLONASS_FREQ[sn - 1]
}


/**
 * 根据JS时间戳获取GPS的跳秒数
 * @param {*} utc UTC时区的JS时间戳
 * @returns Galileo时间系统的跳秒
 */
function getGSTLeapSecond(utc) {
    let u = parseInt(utc);
    let leapseconds = 0
    if (u &lt;= 0 || typeof utc !== 'number') {
        return leapseconds;
    }

    for (let i = 0; i &lt; 5; i++) {
        let leap = GPST_LEAPS[i]
        let t = new Date(Date.UTC(leap[0], leap[1], leap[2])).getTime()
        if (u - t > 0) {
            leapseconds = Math.abs(leap[3])
            break;
        }
    }

    return leapseconds
}


function dot(a, b, n) {
    let c = 0.0;
    while (--n >= 0) c += a[n] * b[n];
    return c;
}

/**
 * 将经纬度坐标转换成地心地固系坐标
 * @param {*} pos [lat, lon, h] (弧度,米)
 * @returns 地心地固系坐标 [x,y,z]
 * @see ecef2pos
 */
function pos2ecef(pos) {
    let sinp = Math.sin(pos[0]), cosp = Math.cos(pos[0]), sinl = Math.sin(pos[1]), cosl = Math.cos(pos[1]);
    let e2 = FE_WGS84 * (2.0 - FE_WGS84), v = RE_WGS84 / Math.sqrt(1.0 - e2 * sinp * sinp);

    let r = [0, 0, 0]
    r[0] = (v + pos[2]) * cosp * cosl;
    r[1] = (v + pos[2]) * cosp * sinl;
    r[2] = (v * (1.0 - e2) + pos[2]) * sinp;
    return r;
}

/**
 * 将地心地固系坐标转换成经纬度坐标
 * @param {*} r [x, y, z] (弧度,米)
 * @returns 经纬度坐标 [lat, lon, h] (弧度，米)
 * @see pos2ecef
 */
function ecef2pos(r) {
    let e2 = FE_WGS84 * (2.0 - FE_WGS84), r2 = dot(r, r, 2), z, zk, v = RE_WGS84, sinp;
    let pos = []
    for (z = r[2], zk = 0.0; Math.abs(z - zk) >= 1E-4;) {
        zk = z;
        sinp = z / Math.sqrt(r2 + z * z);
        v = RE_WGS84 / Math.sqrt(1.0 - e2 * sinp * sinp);
        z = r[2] + v * e2 * sinp;
    }
    pos.push(r2 > 1E-12 ? Math.atan(z / Math.sqrt(r2)) : (r[2] > 0.0 ? PI / 2.0 : -PI / 2.0)); //rad
    pos.push(r2 > 1E-12 ? Math.atan2(r[1], r[0]) : 0.0); //rad
    pos.push(Math.sqrt(r2 + z * z) - v); //m

    //rad to deg
    //pos[0] = pos[0] * D2R
    //pos[1] = pos[1] * 180 / Math.PI
    return pos;
}


function matmul(tr, n, k, m, alpha, A, B, beta, C) {
    let d;
    let i, j, x, f = tr[0] == 'N' ? (tr[1] == 'N' ? 1 : 2) : (tr[1] == 'N' ? 3 : 4);

    for (i = 0; i &lt; n; i++) for (j = 0; j &lt; k; j++) {
        d = 0.0;
        switch (f) {
            case 1: for (x = 0; x &lt; m; x++) d += A[i + x * n] * B[x + j * m]; break;
            case 2: for (x = 0; x &lt; m; x++) d += A[i + x * n] * B[j + x * k]; break;
            case 3: for (x = 0; x &lt; m; x++) d += A[x + i * m] * B[x + j * m]; break;
            case 4: for (x = 0; x &lt; m; x++) d += A[x + i * m] * B[j + x * k]; break;
        }
        if (beta == 0.0) C[i + j * n] = alpha * d; else C[i + j * n] = alpha * d + beta * C[i + j * n];
    }
}

/**
 * 将获取地心地固系到本地坐标的转换矩阵
 * @param {*} pos [lat, lon]
 * @returns 转换矩阵，长度为9的数组
 * @private
 */
function xyz2enu(pos) {
    let E = [0, 0, 0, 0, 0, 0, 0, 0, 0]
    sinp = Math.sin(pos[0]), cosp = Math.cos(pos[0]), sinl = Math.sin(pos[1]), cosl = Math.cos(pos[1]);

    E[0] = -sinl; E[3] = cosl; E[6] = 0.0;
    E[1] = -sinp * cosl; E[4] = -sinp * sinl; E[7] = cosp;
    E[2] = cosp * cosl; E[5] = cosp * sinl; E[8] = sinp;
    return E
}


/**
 * 将地心地固系坐标转换成ENU坐标
 * @param {*} pos ENU站心点 [lat, lon] (弧度)
 * @param {*} r 需要转换的地心地固系坐标 [x,y,z]
 * @returns ENU坐标[E,N,U]
 * @see enu2ecef
 */
function ecef2enu(pos, r) {
    let E = xyz2enu(pos);
    let e = [0, 0, 0]
    matmul("NN", 3, 1, 3, 1.0, E, r, 0.0, e);
    return e
}

/**
 * 将ENU坐标转换成地心地固系坐标
 * @param {*} pos ENU站心点 [lat, lon] (弧度)
 * @param {*} e 需要转化你的ENU坐标[e,n,u]
 * @returns 地心地固系坐标[x,y,z]
 * @see ecef2enu
 */
function enu2ecef(pos, e) {
    let E = xyz2enu(pos);
    let r = [0, 0, 0]
    matmul("TN", 3, 1, 3, 1.0, E, e, 0.0, r);
    return r
}

/**
 * 将数值从弧度制转换成角度值
 * @param {*} rad 需要转换的数值
 * @returns 转换成角度制后的数值
 * @see deg2rad
 */
function rad2deg(rad) {
    return rad * R2D
}

/**
 * 将数值从角度制转换成弧度制
 * @param {*} deg 需要转换的数值
 * @returns 转换成弧度制后的数值
 * @see rad2deg
 */
function deg2rad(deg) {
    return deg * D2R
}

/**
 * 将度分秒数值转换成度
 * @param {*} deg 度
 * @param {*} min 分
 * @param {*} sec 秒
 * @return 角度值数值
 * @see deg2dms
 */
function dms2deg(deg, min, sec) {
    let sign = deg &lt; 0.0 ? -1.0 : 1.0;
    return sign * (Math.abs(deg) + min / 60.0 + sec / 3600.0);
}

/**
 * 将度转换成度分秒数据
 * @param {*} deg 角度
 * @param {*} ndec 秒的小数位数
 * @return 度分秒数组 [d,m,s]
 * @see dms2deg
 */
function deg2dms(deg, ndec) {
    let sign = deg &lt; 0.0 ? -1.0 : 1.0, a = Math.abs(deg);
    let unit = Math.pow(0.1, ndec || 6);
    let dms = new Array(3)
    dms[0] = Math.floor(a); a = (a - dms[0]) * 60.0;
    dms[1] = Math.floor(a); a = (a - dms[1]) * 60.0;
    dms[2] = Math.floor(a / unit + 0.5) * unit;
    if (dms[2] >= 60.0) {
        dms[2] = 0.0;
        dms[1] += 1.0;
        if (dms[1] >= 60.0) {
            dms[1] = 0.0;
            dms[0] += 1.0;
        }
    }
    dms[0] *= sign;
    return dms
}

function utc2local(date) {
    return date.getTime() - TZ_OFFSET
}

function local2utc(date) {
    return date.getTime() + TZ_OFFSET
}

/**
 * UTC时间转换成GPST时间
 * @returns GPST时间的毫秒数
 * @private
 */
function utc2gpst(date) {
    let utc = date.getTime()
    let d = new Date(utc)
    d.setSeconds(d.getSeconds() + getGPSLeapSecond(utc))
    return d.getTime()
}

/**
 * UTC时间转换成GST时间
 * @returns GST时间的毫秒数
 * @private
 */
function utc2gst(date) {
    let utc = date.getTime()
    let d = new Date(utc)
    d.setSeconds(d.getSeconds() + getGSTLeapSecond(utc))
    return d.getTime()
}

/**
 * UTC时间转换成BDT时间
 * @returns BDT时间的毫秒数
 * @private
 */
function utc2bdt(date) {
    return utc2gpst(date) - 14 * 1000
}

/**
 * GPS时间转换成UTC时间
 * @returns UTC时间的毫秒数
 * @private
 */
function gpst2utc(date) {
    let utc = date.getTime()
    date.setSeconds(date.getSeconds() - getGPSLeapSecond(utc))
    return date.getTime()
}

/**
 * GST时间转换成UTC时间
 * @returns UTC时间的毫秒数
 * @private
 */
function gst2utc(date) {
    let utc = date.getTime()
    date.setSeconds(date.getSeconds() - getGSTLeapSecond(utc))
    return date.getTime()
}

/**
 * BD时间转换成GPS时间
 * @returns UTC时间的毫秒数
 * @private
 */
function bdt2gpst(date) {
    let d = date.getTime() + 14 * 1000
    return d
}

/**
 * @description 将Date对象转换成Galileo 周和周内秒的时间
 * @param {*} date Galileo时间Date对象
 * @returns 周和周内秒 [w, sow]
 * 
 * @see toGST
 * @see Date.GST
 */
function date2gst(date) {
    let t = date.getTime()
    let duration = t + UTC0 - GALT0
    let w = parseInt(duration / WEEK_MSEC)
    let sow = duration % WEEK_MSEC
    return [w, sow]
}

/**
 * 将Date对象转换成GPS 周和周内秒的时间
 * @param {*} date GPS时间Date对象
 * @returns 周和周内秒 [w, sow]
 * 
 * @see toGPST
 * @see Date.GPST
 */
function date2gpst(date) {
    let t = date.getTime()
    let duration = t + UTC0 - GPST0
    let w = parseInt(duration / WEEK_MSEC)
    let sow = duration % WEEK_MSEC
    return [w, sow]
}

/**
 * 
 * 将Date对象转换成BD 周和周内秒的时间
 * @param {*} date 北斗时间Date对象
 * @returns 周和周内秒 [w, sow]
 * @see toBDT
 * @see Date.BDT
 */
function date2bdt(date) {
    let t = date.getTime()
    let duration = t + UTC0 - BDT0
    let w = parseInt(duration / WEEK_MSEC)
    let sow = duration % WEEK_MSEC
    return [w, sow]
}

module.exports = {
    /**
     * 根据Glonass 卫星号返回该卫星号对应的载频编号（K）/频率索引(-7~6)
     * @param {*} sateliteNumber Glonass卫星号（1~24）
     * @returns 对应的载频编号/频率索引，错误数据返回undefined
     * @see https://www.cnblogs.com/caimagic/p/11754470.html
     */
    getGlonassFrequency,
    /**
     * 根据JS时间戳获取GPS的跳秒数
     * @param {*} utc UTC时区的JS时间戳
     * @returns GPS跳秒
     */
    getGPSLeapSecond,
    /**
     * 根据JS时间戳获取GST的跳秒数
     * @param {*} utc UTC时区的JS时间戳
     * @returns GST(Galileo)跳秒
     */
    getGSTLeapSecond,
    /**
     * 将经纬度坐标转换成地心地固系坐标
     * @param {*} pos [lat, lon, h] (弧度,米)
     * @returns 地心地固系坐标 [x,y,z]
     */
    pos2ecef,
    /**
     * 将地心地固系坐标转换成经纬度坐标
     * @param {*} r [x, y, z] (弧度,米)
     * @returns 经纬度坐标 [lat, lon, h]
     */
    ecef2pos,
    /**
     * 将地心地固系坐标转换成ENU坐标
     * @param {*} pos ENU站心点 [lat, lon] (弧度)
     * @param {*} r 需要转换的地心地固系坐标 [x,y,z]
     * @returns ENU坐标[E,N,U]
     */
    ecef2enu,
    /**
     * 将ENU坐标转换成地心地固系坐标
     * @param {*} pos ENU站心点 [lat, lon] (弧度)
     * @param {*} e 需要转化你的ENU坐标[e,n,u]
     * @returns 地心地固系坐标[x,y,z]
     */
    enu2ecef,
    /**
     * 将数值从弧度制转换成角度值
     * @param {*} rad 需要转换的数值
     * @returns 转换成角度制后的数值
     */
    rad2deg,

    /**
     * 将数值从角度制转换成弧度制
     * @param {*} deg 需要转换的数值
     * @returns 转换成弧度制后的数值
     */
    deg2rad,
    /**
     * 将度分秒数值转换成度
     * @param {*} deg 度
     * @param {*} min 分
     * @param {*} sec 秒
     * @return 角度值数值
     */
    dms2deg,
    /**
     * 将度转换成度分秒数据
     * @param {*} deg 角度
     * @return 度分秒数组 [d,m,s]
     */
    deg2dms,
    /**
     * 将Date对象转换成BD 周和周内秒的时间
     * @param {*} date 北斗时间Date对象
     * @returns 周和周内秒 [w, sow]
     * @see toBDT
     * @see Date.BDT
     */
    date2bdt,
    /**
     * 将Date对象转换成Galileo 周和周内秒的时间
     * @param {*} date Galileo时间Date对象
     * @returns 周和周内秒 [w, sow]
     * 
     * @see toGST
     * @see Date.GST
     */
    date2gst,
    /**
     * 将Date对象转换成GPS 周和周内秒的时间
     * @param {*} date GPS时间Date对象
     * @returns 周和周内秒 [w, sow]
     * 
     * @see toGPST
     * @see Date.GPST
     */
    date2gpst,
    /**
     * Rinex文件的卫星系统定义字符('G','C','R','E'...)
     * 来源于Rinex 3.02
     */
    RinexSSI: {
        'GPS': GPS,
        'Beidou': BDS,
        'Glonass': GLO,
        'Galileo': GAL,
        'SBAS': SBAS,
        'QZSS': QZSS,
        'IRNSS': IRNSS
    }
};</code></pre>
        </article>
    </section>




</div>

<nav>
    <h2><a href="index.html">Home</a></h2><h3>Global</h3><ul><li><a href="global.html#date2bdt">date2bdt</a></li><li><a href="global.html#date2gpst">date2gpst</a></li><li><a href="global.html#date2gst">date2gst</a></li><li><a href="global.html#deg2dms">deg2dms</a></li><li><a href="global.html#deg2rad">deg2rad</a></li><li><a href="global.html#dms2deg">dms2deg</a></li><li><a href="global.html#ecef2enu">ecef2enu</a></li><li><a href="global.html#ecef2pos">ecef2pos</a></li><li><a href="global.html#enu2ecef">enu2ecef</a></li><li><a href="global.html#getGPSLeapSecond">getGPSLeapSecond</a></li><li><a href="global.html#getGSTLeapSecond">getGSTLeapSecond</a></li><li><a href="global.html#getGlonassFrequency">getGlonassFrequency</a></li><li><a href="global.html#pos2ecef">pos2ecef</a></li><li><a href="global.html#rad2deg">rad2deg</a></li></ul>
</nav>

<br class="clear">

<footer>
    Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 4.0.2</a> on Mon Aug 28 2023 17:03:41 GMT+0800 (中国标准时间)
</footer>

<script> prettyPrint(); </script>
<script src="scripts/linenumber.js"> </script>
</body>
</html>
